import json
import os
from typing import Callable
from datetime import datetime
from pathlib import Path

from fastapi import FastAPI, Request, Response
from fastapi.exceptions import RequestValidationError
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from starlette.middleware.base import BaseHTTPMiddleware
from loguru import logger

from apps.system.models import Visitor
from utils.deps import get_client_info

# 设置日志配置
current_file = Path(__file__)
project_root = current_file.parent.parent
log_path = project_root / "logs"
log_path.mkdir(exist_ok=True)

logger.add(
    log_path / "success.log",
    rotation="1 day",
    level="INFO",
    filter=lambda record: record["extra"].get("status_code", 500) < 400,
)
logger.add(
    log_path / "error.log",
    rotation="1 day",
    level="ERROR",
    filter=lambda record: record["extra"].get("status_code", 500) >= 400,
)


class CustomMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next: Callable) -> Response:
        # 排除 OpenAPI 相关路径
        if request.url.path in ["/docs", "/redoc", "/openapi.json"]:
            return await call_next(request)

        try:
            response = await call_next(request)

            # 如果响应不是 JSON 格式，直接返回原始响应
            if response.headers.get("content-type") != "application/json":
                return response

            response_body = b""
            async for chunk in response.body_iterator:
                response_body += chunk

            try:
                response_data = json.loads(response_body.decode())
            except json.JSONDecodeError:
                response_data = response_body.decode()

            if response.status_code >= 400:
                return JSONResponse(
                    status_code=200,
                    content={
                        "code": response.status_code,
                        "msg": response_data.get("detail", "未知错误"),
                        "data": None,
                    },
                )
            if (
                    "docs" in request.headers.get("referer", "")
                    and request.url.path == "/api/auth/token"
            ):
                return JSONResponse(
                    status_code=200,
                    content=response_data,
                )

            return JSONResponse(
                status_code=200,
                content={"code": 200, "msg": "Success", "data": response_data},
            )
        except Exception as e:
            return JSONResponse(
                status_code=200, content={"code": 500, "msg": str(e), "data": None}
            )


class VisitorMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next: Callable) -> Response:
        if request.url.path in ["/docs", "/redoc", "/openapi.json"]:
            return await call_next(request)

        client_info = get_client_info(request)
        if client_info.get("ip") == "127.0.0.1" or client_info.get("ip") == "localhost":
            return await call_next(request)

        data = {
            "ip": client_info.get("ip"),
            "country": client_info.get("country"),
            "region": client_info.get("region"),
            "city": client_info.get("city"),
        }

        # 获取最新的访客记录
        visitor = await Visitor.filter(ip=client_info.get("ip")).order_by("-id").first()

        if visitor:
            # 如果记录存在，更新访问次数
            await Visitor.filter(id=visitor.id).update(count=visitor.count + 1)
        else:
            # 如果记录不存在，创建新记录
            await Visitor.create(count=1, **data)

        return await call_next(request)


class LogMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next: Callable) -> Response:
        start_time = datetime.now()

        try:
            response: Response = await call_next(request)
            process_time = (datetime.now() - start_time).total_seconds()
            status_code = response.status_code
            log_msg = f"Request: {request.method} {request.url} - Status: {status_code} - Process Time: {process_time:.2f}s"

            if status_code < 400:
                logger.info(log_msg, status_code=status_code)
            else:
                logger.error(log_msg, status_code=status_code)

            return response
        except Exception as exc:
            process_time = (datetime.now() - start_time).total_seconds()
            log_msg = f"Request: {request.method} {request.url} - Status: 500 - Process Time: {process_time:.2f}s - Error: {str(exc)}"
            logger.error(log_msg, status_code=500, exc_info=True)
            raise


def register_middleware(app: FastAPI):
    app.add_middleware(CustomMiddleware)
    app.add_middleware(
        CORSMiddleware,
        allow_origins=["*"],
        allow_credentials=True,
        allow_methods=["*"],
        allow_headers=["*"],
    )
    app.add_middleware(LogMiddleware)
    app.add_middleware(VisitorMiddleware)


def register_exception_handler(app: FastAPI):
    @app.exception_handler(Exception)
    async def global_exception_handler(request: Request, exc: Exception):
        if os.getenv("ENV") == "production":
            log_msg = f"Request: {request.method} {request.url} - Error: {str(exc)}"
            logger.error(log_msg, status_code=500, exc_info=True)
            return JSONResponse(
                status_code=200,
                content={
                    "code": 500,
                    "msg": "服务器内部错误",
                    "data": None,
                },
            )
        else:
            log_msg = f"Request: {request.method} {request.url} - Error: {str(exc)}"
            logger.error(log_msg, status_code=500, exc_info=True)
            return JSONResponse(
                status_code=200,
                content={
                    "code": 500,
                    "msg": str(exc),
                    "data": None,
                },
            )


def register_validation_exception_handler(app: FastAPI):
    @app.exception_handler(RequestValidationError)
    async def validation_exception_handler(
            request: Request, exc: RequestValidationError
    ):
        detail = exc.errors()[0].get("msg")
        log_msg = (
            f"Request: {request.method} {request.url} - Validation Error: {detail}"
        )
        logger.error(log_msg, status_code=422, exc_info=True)
        return JSONResponse(
            status_code=422,
            content={"detail": detail},
        )


def setup_middleware_and_exception_handler(app: FastAPI):
    register_middleware(app)
    register_exception_handler(app)
    register_validation_exception_handler(app)
